package com.yupi.springbootinit.service.impl;

import static com.yupi.springbootinit.constant.UserConstant.USER_LOGIN_STATE;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yupi.springbootinit.common.ErrorCode;
import com.yupi.springbootinit.constant.CommonConstant;
import com.yupi.springbootinit.constant.UserConstant;
import com.yupi.springbootinit.exception.BusinessException;
import com.yupi.springbootinit.manager.SessionManager;
import com.yupi.springbootinit.mapper.UserMapper;
import com.yupi.springbootinit.model.dto.user.UserQueryRequest;
import com.yupi.springbootinit.model.entity.User;
import com.yupi.springbootinit.model.enums.UserRoleEnum;
import com.yupi.springbootinit.model.vo.LoginUserVO;
import com.yupi.springbootinit.model.vo.UserVO;
import com.yupi.springbootinit.service.AnonnameService;
import com.yupi.springbootinit.service.UserService;
import com.yupi.springbootinit.utils.NetUtils;
import com.yupi.springbootinit.utils.SqlUtils;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

import com.yupi.springbootinit.utils.ThumbNailUtil;
import lombok.extern.slf4j.Slf4j;
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.web.multipart.MultipartFile;

/**
 * 用户服务实现
 */
@Service
@Slf4j
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    /**
     * 盐值，混淆密码
     */
    public static final String SALT = "yupi";

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public long userRegister(String userAccount, String userPassword, String checkPassword, String phoneNumber, String checkedCode) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword, phoneNumber, checkedCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }
        if (phoneNumber.length() < 10) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入正确的手机号");
        }

        List<String> sensitiveWords = Arrays.asList(// 违反法律法规类
                "法轮功", "全能神", "华藏宗门", "冰毒", "海洛因", "大麻", "制毒", "贩毒", "P2P暴雷", "虚拟货币挖矿", "民间标会倒会", "套路贷",
                // 危害国家安全与社会稳定类
                "港独", "台独", "蒙独", "疆独", "反动会道门", "恶意诋毁国家政策", "抹黑医保改革", "唱衰国企",
                // 低俗色情类
                "阴茎", "睾丸", "阴道", "阴蒂", "做爱", "性交", "口交", "肛交", "自慰", "一夜情", "嫖娼", "约炮", "乳沟", "翘臀", "丝袜诱惑",
                "苍井空", "波多野结衣", "小泽玛利亚", "武藤兰", "AV女优", "成人影片", "三级片", "黄片", "色情直播", "裸聊", "露点", "性感内衣秀",
                "性癖好", "性虐待", "SM", "淫荡", "风骚", "狐狸精", "荡妇", "牛郎", "鸭店", "色情按摩", "情趣用品",
                "AV", "ML", "FJ", "KJ", "约P", "PC", "黄播", "色Q",
                // 暴力恐怖类
                "基地组织", "伊斯兰国", "博科圣地", "斩首", "肢解", "枪杀", "纵火", "爆炸", "绑架勒索", "持械抢劫",
                // 网络诈骗类
                "刷单返现骗局", "网络兼职诈骗", "冒充公检法诈骗", "交友杀猪盘", "荐股诈骗", "跑分", "赌博", "彩票",
                // 知识产权侵权类
                "山寨苹果手机", "假耐克运动鞋", "盗版微软系统", "未经授权翻拍某电影", "盗版某热门小说", "盗录某演唱会音频",
                // 人身攻击与歧视类
                "黑鬼", "白皮猪", "男尊女卑", "女权癌", "娘炮", "弱女子", "瘸子", "瞎子", "聋子", "弱智",
                // 不实信息与谣言类
                "某地发生9级地震", "某明星去世", "吃绿豆治百病", "喝醋软化血管", "永动机研发成功", "量子速读提升学习能力",
                // 广告垃圾信息类
                "特效抗癌药", "根治糖尿病神药", "一周瘦十斤减肥药", "三个月长高10厘米增高药", "加微信免费领礼品", "点击链接获取巨额奖金"); // 示例敏感词列表
        if (containsSensitiveWords(userAccount, sensitiveWords)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容包含敏感词");
        }

        String redisCheckedCode = redisTemplate.opsForValue().get("yzm" + phoneNumber);
        if (!checkedCode.equals(redisCheckedCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
        }

        synchronized (userAccount.intern()) {
            // 账户不能重复
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("userAccount", userAccount);
            long count = this.baseMapper.selectCount(queryWrapper);
            if (count > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户名已存在");
            }

            QueryWrapper<User> queryWrapper2 = new QueryWrapper<>();
            queryWrapper2.eq("phoneNum", phoneNumber);
            long count2 = this.baseMapper.selectCount(queryWrapper2);
            if (count2 > 0) {
                throw new BusinessException(ErrorCode.PARAMS_ERROR, "手机号已存在");
            }
            // 2. 加密
            String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
            // 3. 插入数据
            User user = new User();
            user.setUserAccount(userAccount);
            user.setUserPassword(encryptPassword);
            user.setUserName(userAccount);
            user.setPhoneNum(phoneNumber);
            user.setUserPoints(20);
            boolean saveResult = this.save(user);
            if (!saveResult) {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败，数据库错误");
            }
            return user.getId();
        }
    }

    /**
     * 判断文本是否包含敏感词
     *
     * @param text
     * @param sensitiveWords
     * @return
     */
    private boolean containsSensitiveWords(String text, List<String> sensitiveWords) {
        if (StringUtils.isBlank(text)) {
            return false;
        }
        for (String word : sensitiveWords) {
            if (text.contains(word)) {
                return true;
            }
        }
        return false;
    }

    @Resource
    private AnonnameService anonnameService;

    @Resource
    private SessionManager sessionManager;


    @Override
    public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) {
        // 1. 校验
        if (StringUtils.isAnyBlank(userAccount, userPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 查询用户是否存在
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        //用户名或者手机号都可以，都必须唯一
        queryWrapper.eq("userAccount", userAccount).or().eq("phoneNum", userAccount);
        queryWrapper.eq("userPassword", encryptPassword);
        User user = this.baseMapper.selectOne(queryWrapper);
        // 用户不存在
        if (user == null) {
            log.info("user login failed, userAccount cannot match userPassword");
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误");
        }
        if (user.getUserRole().equals(UserConstant.BAN_ROLE)){
            throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "该用户已被封，禁止登录");
        }
        // 3. 记录用户的登录态
        sessionManager.login(user, request);

        String anonname = anonnameService.getAnonname();
        user.setAnon_name(anonname);
        if (user.getIsSign() == 0) {
            user.setUserPoints(user.getUserPoints() + 1);
            user.setIsSign(1);
        }
        // 应考虑添加业务逻辑或检查以确保此更新操作是安全的
        this.updateById(user);
//        request.getSession().setAttribute(USER_LOGIN_STATE, user);
        return this.getLoginUserVO(user);
    }

    @Override
    public LoginUserVO userLoginByMpOpen(WxOAuth2UserInfo wxOAuth2UserInfo, HttpServletRequest request) {
        String unionId = wxOAuth2UserInfo.getUnionId();
        String mpOpenId = wxOAuth2UserInfo.getOpenid();
        // 单机锁
        synchronized (unionId.intern()) {
            // 查询用户是否已存在
            QueryWrapper<User> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("unionId", unionId);
            User user = this.getOne(queryWrapper);
            // 被封号，禁止登录
            if (user != null && UserRoleEnum.BAN.getValue().equals(user.getUserRole())) {
                throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "该用户已被封，禁止登录");
            }
            // 用户不存在则创建
            if (user == null) {
                user = new User();
                user.setUnionId(unionId);
                user.setMpOpenId(mpOpenId);
                user.setUserAvatar(wxOAuth2UserInfo.getHeadImgUrl());
                user.setUserName(wxOAuth2UserInfo.getNickname());
                boolean result = this.save(user);
                if (!result) {
                    throw new BusinessException(ErrorCode.SYSTEM_ERROR, "登录失败");
                }
            }

            String anonname = anonnameService.getAnonname();
            user.setAnon_name(anonname);

            // 明确指定更新的字段，避免误更新其他字段
            User userToUpdate = new User();
            userToUpdate.setAnon_name(anonname);

            // 使用LambdaQueryWrapper以避免硬编码字段名
            LambdaQueryWrapper<User> queryWrapper1 = new LambdaQueryWrapper<>();
            queryWrapper1.eq(User::getAnon_name, anonname);

            // 应考虑添加业务逻辑或检查以确保此更新操作是安全的
            boolean updateResult = this.update(userToUpdate, queryWrapper);
            if (!updateResult) {
                // 适当的错误处理或日志记录
                log.error("Failed to update user's anon name.");
            }
            // 记录用户的登录态
            request.getSession().setAttribute(USER_LOGIN_STATE, user);
            return getLoginUserVO(user);
        }
    }

    /**
     * 获取当前登录用户
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUser(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            throw new RuntimeException("未登录");
        }
        // 校验是否已在其他客户端登录
        String ipAddress = NetUtils.getIpAddress(request);
        String oldSessionId = sessionManager.checkOtherLogin(currentUser.getId(), ipAddress, request);
        if (StrUtil.isNotBlank(oldSessionId)) {
            request.getSession().removeAttribute(USER_LOGIN_STATE);
            throw new RuntimeException("已在其他设备登录，请重新登录");
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        currentUser = this.getById(userId);
        if (currentUser == null) {
            throw new RuntimeException("当前未登录");
        }
        if (currentUser.getIsSign() == 0) {
            currentUser.setUserPoints(currentUser.getUserPoints() + 1);
            currentUser.setIsSign(1);
            this.updateById(currentUser);
        }
        return currentUser;
    }


//    @Override
//    public User getLoginUser(HttpServletRequest request) {
//        // 先判断是否已登录
//        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
//        User currentUser = (User) userObj;
//        if (currentUser == null || currentUser.getId() == null) {
//            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
//        }
//        // 从数据库查询（追求性能的话可以注释，直接走缓存）
//        long userId = currentUser.getId();
//        currentUser = this.getById(userId);
//        if (currentUser == null) {
//            throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR);
//        }
//        return currentUser;
//    }

    /**
     * 获取当前登录用户（允许未登录）
     *
     * @param request
     * @return
     */
    @Override
    public User getLoginUserPermitNull(HttpServletRequest request) {
        // 先判断是否已登录
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User currentUser = (User) userObj;
        if (currentUser == null || currentUser.getId() == null) {
            return null;
        }
        // 从数据库查询（追求性能的话可以注释，直接走缓存）
        long userId = currentUser.getId();
        return this.getById(userId);
    }

    /**
     * 是否为管理员
     *
     * @param request
     * @return
     */
    @Override
    public boolean isAdmin(HttpServletRequest request) {
        // 仅管理员可查询
        Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE);
        User user = (User) userObj;
        return isAdmin(user);
    }

    @Override
    public boolean isAdmin(User user) {
        return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole());
    }

    /**
     * 用户注销
     *
     * @param request
     */
    @Override
    public boolean userLogout(HttpServletRequest request) {
        if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) {
            throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录");
        }
        // 移除登录态
//        request.getSession().removeAttribute(USER_LOGIN_STATE);
        sessionManager.removeAttribute(request, USER_LOGIN_STATE);
        return true;
    }

    @Override
    public LoginUserVO getLoginUserVO(User user) {
        if (user == null) {
            return null;
        }
        LoginUserVO loginUserVO = new LoginUserVO();
        BeanUtils.copyProperties(user, loginUserVO);
        return loginUserVO;
    }

    @Override
    public UserVO getUserVO(User user) {
        if (user == null) {
            return null;
        }
        UserVO userVO = new UserVO();
        BeanUtils.copyProperties(user, userVO);
        return userVO;
    }

    @Override
    public List<UserVO> getUserVO(List<User> userList) {
        if (CollUtil.isEmpty(userList)) {
            return new ArrayList<>();
        }
        return userList.stream().map(this::getUserVO).collect(Collectors.toList());
    }

    @Override
    public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) {
        if (userQueryRequest == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空");
        }
        Long id = userQueryRequest.getId();
        String unionId = userQueryRequest.getUnionId();
        String mpOpenId = userQueryRequest.getMpOpenId();
        String userName = userQueryRequest.getUserName();
        String userProfile = userQueryRequest.getUserProfile();
        String userRole = userQueryRequest.getUserRole();
        String sortField = userQueryRequest.getSortField();
        String sortOrder = userQueryRequest.getSortOrder();
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq(id != null, "id", id);
        queryWrapper.eq(StringUtils.isNotBlank(unionId), "unionId", unionId);
        queryWrapper.eq(StringUtils.isNotBlank(mpOpenId), "mpOpenId", mpOpenId);
        queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole);
        queryWrapper.like(StringUtils.isNotBlank(userProfile), "userProfile", userProfile);
        queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName);
        queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC),
                sortField);
        return queryWrapper;
    }

    @Value("${userFilePath}")
    private String userImgPath;

    @Override
    public String uploadUserImage(MultipartFile file) {
        String fileName = null;
        String imageName = file.getOriginalFilename();
        if (imageName == null || imageName.isEmpty()) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR);
        }

        String[] split = imageName.split("\\.");
        String imgType = split[split.length - 1];
        UUID randomUUID = UUID.randomUUID();
        fileName = randomUUID + "." + imgType;
        String filePath = userImgPath + fileName;
        ThumbNailUtil.thumbnail(file, filePath,imgType);
        return fileName;
    }

    @Override
    public boolean deleteUserImage(String fileName) {
        String filePath = userImgPath + fileName;

        File imageFile = new File(filePath);
        if (imageFile.exists()) {
            boolean success = imageFile.delete();
            if (success) {
                return true;
            } else {
                throw new BusinessException(ErrorCode.SYSTEM_ERROR, "删除图片失败");
            }
        } else {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "图片不存在");
        }
    }


    @Resource
    private UserMapper userMapper;

    /**
     * 积分-1
     */
    @Override
    public boolean reduceOnePoint(User user) {
        user.setUserPoints(user.getUserPoints() - 1);
        int i = userMapper.updateById(user);
        if (i > 0) {
            return true;
        }
        return false;
    }

    @Override
    public void resetAllUserSignsToZero() {


        userMapper.resetAllSignsToZero();
    }

    @Override
    public boolean forgetPassword(String userPhoneNum, String userPassword, String checkPassword, String checkCode) {

        // 1. 校验
        if (StringUtils.isAnyBlank(userPhoneNum, userPassword, checkPassword, checkCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空");
        }
        if (userPhoneNum.length() < 10||userPhoneNum.length() > 12) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "请输入正确的手机号");
        }
        // 密码和校验密码相同
        if (!userPassword.equals(checkPassword)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致");
        }


        String redisCheckedCode = redisTemplate.opsForValue().get("yzm" + userPhoneNum);
        if (!checkCode.equals(redisCheckedCode)) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "验证码错误");
        }

        // 账户不能重复
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("phoneNum", userPhoneNum);
        User user = this.getOne(queryWrapper);
        if (user == null) {
            throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在");
        }
        // 2. 加密
        String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes());
        // 3. 插入数据
        user.setUserPassword(encryptPassword);
        boolean b = this.updateById(user);
        return b;
    }
}
